package com.android.yummycookies.asyncTask;

import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.SparseArray;

import com.android.yummycookies.BuildConfig;
import com.android.yummycookies.R;
import com.android.yummycookies.model.commonModel.ObserversPool;
import com.android.yummycookies.storage.BitmapStorage;
import com.android.yummycookies.util.Constant;
import com.android.yummycookies.util.Event;
import com.android.yummycookies.util.Logger;

/**
 * Classe permetant le chargement asynchrone de bitmap
 * 
 * @author julien
 */
public class LoadBitmapThread extends Thread {

	public static final String TAG = LoadBitmapThread.class.getSimpleName();

	/*****************************************************************
	 * Constantes de chargement de bitmap
	 *****************************************************************/

	/**
	 * Constante indiquant le debut du chargement des bitmap
	 */
	public static final int START_LOADING_BITMAPS = Event.START_LOADING_BITMAPS;
	/**
	 * Constante indiquant qu'un bitmap a ete charge
	 */
	public static final int BITMAP_LOADED = Event.BITMAP_LOADED;
	/**
	 * Constante indiquant qu'un bitmap n'a pas pu etre charge
	 */
	public static final int BITMAP_NOT_LOADED = Event.BITMAP_NOT_LOADED;
	/**
	 * Constante indiquant la fin du chargement des bitmap
	 */
	public static final int END_LOADING_BITMAPS = Event.END_LOADING_BITMAPS;

	/*****************************************************************
	 * Contexte de persistance
	 *****************************************************************/

	/** Context applicatif */
	private final Context context;
	/** BitmapStorage applicatif */
	private final BitmapStorage bitmapStorage;

	/*****************************************************************
	 * Collections representant l'avancement du chargement des bitmap
	 *****************************************************************/

	/** Set contenant les identifiants des ressources drawable a charger */
	private final Set<Integer> drawablesToLoad;
	/** Map contenant les bitmap qui ont ete charges */
	private final SparseArray<Bitmap> loadedBitmaps = new SparseArray<Bitmap>();

	/** Ensemble des identifiants des bitmap en cours de chargement */
	private final Set<Integer> loadingBitmaps;

	/******************************************************************
	 * Gestion des messages
	 ******************************************************************/
	private final ObserversPool observablePool = new ObserversPool();

	/**
	 * Constructeur du thread de chargement de bitmap
	 * 
	 * @param context
	 *            le context applicatif
	 * @param bitmapStorage
	 *            le bitmapStorage qui contiendra les bitmap
	 * @param loadingBitmaps
	 *            un ensemble d'id de bitmap permetant d'eviter de charger
	 *            plusieurs fois un bitmap
	 * @param drawableId
	 *            un identifiant de drawable a charger
	 */
	public LoadBitmapThread(Context context, BitmapStorage bitmapStorage,
			Set<Integer> loadingBitmaps, int drawableId) {

		this.context = context;
		this.bitmapStorage = bitmapStorage;
		this.loadingBitmaps = loadingBitmaps;
		this.drawablesToLoad = new HashSet<Integer>();
		this.drawablesToLoad.add(drawableId);

		Logger.logDebug(TAG, TAG + " created");
	}

	/**
	 * Constructeur du thread de chargement de bitmap
	 * 
	 * @param context
	 *            le context applicatif
	 * @param bitmapStorage
	 *            le bitmapStorage qui contiendra les bitmap
	 * @param loadingBitmaps
	 *            un ensemble d'id de bitmap permetant d'eviter de charger
	 *            plusieurs fois un bitmap
	 * @param drawableIds
	 *            un ensemble d'identifiants de bitmap a charger
	 */
	public LoadBitmapThread(Context context, BitmapStorage bitmapStorage,
			Set<Integer> loadingBitmaps, Set<Integer> drawableIds) {

		this.context = context;
		this.bitmapStorage = bitmapStorage;
		this.loadingBitmaps = loadingBitmaps;
		this.drawablesToLoad = drawableIds;

		Logger.logDebug(TAG, TAG + " created");
	}

	/*****************************************************************
	 * Getteurs
	 *****************************************************************/

	/**
	 * Renvoie le pool d'observeurs
	 * 
	 * @return un pool d'observeurs
	 */
	public ObserversPool getObservablePool() {
		return observablePool;
	}

	@Override
	public void run() {
		sendStartMessage();
		// charge tous les bitmap
		for (Integer drawableId : drawablesToLoad) {
			// chargement du bitmap si il n'est pas dans le
			// bitmapStorage
			if (!bitmapStorage.isStored(drawableId)) {
				// ajoute l'id du bitmap dans l'ensemble des bitmap
				// en cours de chargement
				boolean isLoading = false;
				synchronized (loadingBitmaps) {
					isLoading = loadingBitmaps.contains(drawableId);
					if (!isLoading) {
						loadingBitmaps.add(drawableId);
					}
				}

				// si le bitmap n'est pas en cours de chargement
				if (!isLoading) {
					// on decode le bitmap
					Bitmap b = BitmapFactory.decodeResource(
							context.getResources(), drawableId);
					// ajout du bitmap dans les bitmap charges
					loadedBitmaps.put(drawableId, b);

					// si le bitmap a ete effectivement charge
					if (b != null) {
						// ajout du bitmap charge dans le bitmapStorage
						bitmapStorage.put(drawableId, b);
						// envoie d'un message de chargement de bitmap
						sendLoadedMessage(BITMAP_LOADED, drawableId, b);
					} else {
						// envoie d'un message de non chargement de bitmap
						sendLoadedMessage(BITMAP_NOT_LOADED, drawableId, null);
						Log.e(TAG, "id =>" + drawableId);
						Log.e(TAG, "id donut =>" + R.drawable.donut);
						Log.e(TAG, "id checkboard =>" + R.drawable.checkeroard_cookie);
					}

					// suppression du bitmap des bitmap en cours de chargement
					loadingBitmaps.remove(drawableId);

				} else {
					// le bitmap est deja en cours de chargement dans un autre
					// thread
					Logger.logDebug(TAG, "Bitmap already loading", drawableId);
				}

			} else {
				// le bitmap est deja charge
				Logger.logDebug(TAG, "Bitmap already loaded", drawableId);
			}
		}
		sendEndMessage();
	}

	/***************************************************************************
	 * Gestion des messages
	 ***************************************************************************/

	/**
	 * Envoie un message de debut de chargement de bitmap obj: l'ensemble des id
	 * des bitmap a charger
	 */
	private void sendStartMessage() {
		Logger.logDebug(TAG, "Send start message", drawablesToLoad);
		for (WeakReference<Handler> wh : observablePool.getObservers()) {
			Handler h = wh.get();
			if (h != null) {
				Message m = h.obtainMessage();
				m.what = START_LOADING_BITMAPS;
				m.obj = drawablesToLoad;
				h.sendMessage(m);
			}

		}
	}

	/**
	 * Envoie un message de chargement de bitmap arg1: id du bitmap charge obj:
	 * bitmap charge
	 * 
	 * @param event
	 *            l'etat de reussite du chargement du bitmap
	 * @param drawableId
	 *            l'id du bitmap charge
	 * @param bitmap
	 *            l'objet bitmap charge (null si innexistant)
	 */
	private void sendLoadedMessage(int event, int drawableId, Bitmap bitmap) {
		if (BuildConfig.DEBUG) {
			String state = "failed";
			switch (event) {
			case BITMAP_LOADED:
				state = "succeded";
				break;
			default:
				break;
			}
			Logger.logDebug(TAG, "Send load message: " + state, drawableId);
		}
		for (WeakReference<Handler> wh : observablePool.getObservers()) {
			Handler h = wh.get();
			if (h != null) {
				Message m = h.obtainMessage();
				m.what = event;
				m.arg1 = drawableId;
				m.obj = bitmap;
				h.sendMessage(m);
			}

		}
	}

	/**
	 * Envoie un message de fin de chargement de bitmap obj: un SparseArray de
	 * bitmap charges
	 */
	private void sendEndMessage() {
		Logger.logDebug(TAG, "Send end message", loadedBitmaps);
		for (WeakReference<Handler> wh : observablePool.getObservers()) {
			Handler h = wh.get();
			if (h != null) {
				Message m = new Message();
				m.what = END_LOADING_BITMAPS;
				m.obj = loadedBitmaps;
				h.sendMessage(m);
			}
		}
	}

}
